
/**
 * Java语言里定义类的简单语法如下：
 * <pre>
 *     [修饰符] class 类名 {
 *         零个到多个构造器定义...
 *         零个到多个成员变量...
 *         零个到多个方法...
 *     }
 * </pre>
 * 在上面的语法格式中，修饰符可以是public、final、abstract，或者完全省略这三个修饰符，
 * <p>
 * 定义成员变量的语法格式如下：
 * <pre>
 *     [修饰符] 类型 成员变量名 [=默认值];
 * </pre>
 * 对定义成员变量语法格式的详细说明如下。
 * ➢ 修饰符：修饰符可以省略，也可以是public、protected、private、static、final，其中public、protected、private三个最多只能出现其中之一，
 * 可以与static、final组合起来修饰成员变量。
 * ➢ 类型：类型可以是Java语言允许的任何数据类型，包括基本类型和现在介绍的引用类型。
 * ➢ 成员变量名：成员变量名只要是一个合法的标识符即可，但这只是从语法角度来说的；如果从程序可读性角度来看，
 * 成员变量名应该由一个或多个有意义的单词连缀而成，第一个单词首字母小写，后面每个单词首字母大写，其他字母全部小写，单词与单词之间不要使用任何分隔符。
 * 成员变量用于描述类或对象包含的状态数据，因此成员变量名建议使用英文名词。
 * ➢ 默认值：定义成员变量还可以指定一个可选的默认值。
 * <p>
 * 定义方法的语法格式如下：
 * <pre>
 *     [修饰符] 方法返回值类型 方法名(形参列表)
 *     {
 *         // 由零条到多条可执行性语句组成的方法体
 *     }
 * </pre>
 * 对定义方法语法格式的详细说明如下。
 * ➢ 修饰符：修饰符可以省略，也可以是public、protected、private、static、final、abstract，其中public、protected、private三个最多只能出现其中之一；abstract和final最多只能出现其中之一，它们可以与static组合起来修饰方法。
 * ➢ 方法返回值类型：返回值类型可以是Java语言允许的任何数据类型，包括基本类型和引用类型；如果声明了方法返回值类型，则方法体内必须有一个有效的return语句，该语句返回一个变量或一个表达式，这个变量或者表达式的类型必须与此处声明的类型匹配。除此之外，如果一个方法没有返回值，则必须使用void来声明没有返回值。
 * ➢ 方法名：方法名的命名规则与成员变量的命名规则基本相同，但由于方法用于描述该类或该类的实例的行为特征或功能实现，因此通常建议方法名以英文动词开头。
 * ➢ 形参列表：形参列表用于定义该方法可以接受的参数，形参列表由零组到多组“参数类型 形参名”组合而成，多组参数之间以英文逗号（,）隔开，形参类型和形参名之间以英文空格隔开。一旦在定义方法时指定了形参列表，则调用该方法时必须传入对应的参数值——谁调用方法，谁负责为形参赋值。
 * <p>
 * 构造器是一个特殊的方法，定义构造器的语法格式与定义方法的语法格式很像，定义构造器的语法格式如下：
 * <pre>
 *     [修饰符] 构造器名(形参列表)
 *     {
 *         // 由零条到多条可执行性语句组成的构造器执行体
 *     }
 * </pre>
 * 对定义构造器语法格式的详细说明如下。
 * ➢ 修饰符：修饰符可以省略，也可以是public、protected、private其中之一。
 * ➢ 构造器名：构造器名必须和类名相同。
 * ➢ 形参列表：和定义方法形参列表的格式完全相同。
 * <p>
 * 值得指出的是，构造器既不能定义返回值类型，也不能使用void声明构造器没有返回值。
 * 如果为构造器定义了返回值类型，或使用void声明构造器没有返回值，编译时不会出错，
 * 但Java会把这个所谓的构造器当成方法来处理——它就不再是构造器。
 *
 * <p>编译：javac -encoding UTF-8 Class.java
 * <p>运行：java Class
 */

class Person {
    // 下面定义了两个成员变量
    public String name;
    public int age;

    // 下面定义了一个say方法
    public void say(String content) {
        System.out.println(content);
    }
    // 上面的Person类代码里没有定义构造器，系统将为它提供一个默认的构造器，系统提供的构造器总是没有参数的。
}

/**
 * Java提供了一个this关键字，this关键字总是指向调用该方法的对象。
 * 根据this出现位置的不同，this作为对象的默认引用有两种情形。
 * ➢ 构造器中引用该构造器正在初始化的对象。
 * ➢ 在方法中引用调用该方法的对象。
 */
class Dog {
    // 定义一个jump方法
    public void jump() {
        System.out.println("正在执行jump方法");
    }

    // 定义一个run方法，run方法需要借助jump方法
    public void run() {
        this.jump();
        // this可以省略
//        jump();
    }

    // 定义一个静态成员
    static void info() {
        // 静态成员不能直接访问非静态成员。
//        jump();
    }
}

/**
 * 大部分时候，普通方法访问其他方法、成员变量时无须使用this前缀，
 * 但如果方法里有个局部变量和成员变量同名，但程序又需要在该方法里访问这个被覆盖的成员变量，则必须使用this前缀。
 */
class ThisInConstructor {
    // 定义一个名为foo的成员变量
    public int foo;

    public ThisInConstructor() {
        // 在构造器里定义一个foo变量
        int foo = 6;
        // 使用this代表构造器正在初始化的对象
        // 下面的代码将会把构造器正在初始化的对象foo成员变量设为6
        this.foo = 6;
    }
}

/**
 * 当this作为对象的默认引用使用时，程序可以像访问普通引用变量一样来访问这个this引用，甚至可以把this当成普通方法的返回值。
 */
class ReturnThis {
    public int age;

    public ReturnThis grow() {
        age++;
        // return this返回调用该方法的对象
        return this;
    }
}

public class Class {
    public static void personTest() {
        // 使用Person类定义一个Person类型的变量
        Person p;
        // 通过new关键字调用Person类的构造器，返回一个Person示例
        // 将该Person示例赋给p变量
        p = new Person();

        // 上面代码也可以简写成如下形式：
        // 定义p变量的同时并为p变量赋值
//        Person p = new Person();

        // 访问p的name示例变量，直接为该变量赋值
        p.name = "李四";
        // 调用p的say方法
        p.say("学习Java语言");
        // 直接输出p的name示例变量
        System.out.println(p.name);

        // 把p变量的值赋值给p2变量，也就是将p变量保存的地址值赋给p2变量，
        // 这样p2变量和p变量将指向堆内存里的同一个Person对象。
        Person p2 = p;
        // 如果堆内存里的对象没有任何变量指向该对象，那么程序将无法再访问该对象，这个对象也就变成了垃圾，
        // Java的垃圾回收器将回收该对象，释放该对象所占的内存区。
    }

    public static void thisInConstructorTest() {
        System.out.println(new ThisInConstructor().foo);
    }

    public static void returnThisTest() {
        ReturnThis rt = new ReturnThis();
        // 可以连续调用同一个方法
        rt.grow().grow().grow();
        System.out.println("rt的age成员变量值是：" + rt.age);
        // 注意：使用this作为方法的返回值可以让代码更加简洁，但可能造成实际意义的模糊。
    }

    public static void main(String[] args) {
        personTest();
        thisInConstructorTest();
        returnThisTest();
    }
}
